/*
  This file is part of bnc.

  bnc is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  bnc is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with bnc.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "./manager-gtk.h"

/*
  Prepares the manager.
*/
void managerConstructor (manager* aManager)
{
  aManager->pTable=(char***)malloc(sizeof(char**));
  aManager->pTable[0]=0;
  aManager->pFile=0;
}

char managerOpenBnc (manager* aManager, char* aInput)
{
  /*
    Opens the bnc, reads its header.
  */
  unsigned int lCount, lLength;
  lCount=0;
  lLength=0;

  /*
    Check for the file.
  */
  aManager->pFile=fopen(aInput, "rb");
  if (aManager->pFile==0)
  {
    return 0;
  }

  /*
    Get number of files contained.
  */
  fread(&lCount, 1, 2, aManager->pFile);

  aManager->pHeader=(unsigned char*)malloc(8*lCount+6);

  *(unsigned int*)aManager->pHeader=lCount;

  /*
    Get the header.
  */
  fread(aManager->pHeader+2, 1, 8*lCount+4, aManager->pFile);
  memcpy(&lLength, aManager->pHeader+8*lCount+2, 4);

  return 1;
}

char*** managerListFiles (manager* aManager)
{
  /*
    List out files in the bnc.
  */
  unsigned int i, j, lCount;

  lCount=0;
  memcpy(&lCount, aManager->pHeader, 2);

  /*
    Prepare the table of information about the files.
  */
  aManager->pTable=(char***)realloc(aManager->pTable,
                                    (lCount+1)*sizeof(char**));
  for (i=0; i<lCount; i++)
  {
    aManager->pTable[i]=(char**)malloc(4*sizeof(char*));

    for (j=0; j<4; j++)
      aManager->pTable[i][j]=(char*)malloc(256*sizeof(char));
  }

  aManager->pTable[lCount]=0;

  for (i=0; i<lCount; i++)  //Go through all the files in the archive.
  {
    unsigned int lFileOffset, lFileNameLength, lFileLength;
    unsigned int k, l;

    lFileOffset=0;
    lFileNameLength=0;
    lFileLength=0;
    memcpy(&lFileOffset, aManager->pHeader+8*i+2, 4);
    memcpy(&lFileNameLength, aManager->pHeader+8*i+6, 2);
    memcpy(&lFileLength, aManager->pHeader+8*i+10, 4);

    lFileLength-=lFileOffset+lFileNameLength;

    fseek(aManager->pFile, lFileOffset, SEEK_SET);
    fread(aManager->pTable[i][0],
          1,
	  lFileNameLength*sizeof(char),
	  aManager->pFile);
    aManager->pTable[i][0][lFileNameLength]=0;

    /*
      Whether the file is compressed.
    */
    if (aManager->pHeader[8*i+8]==1)
    {
      aManager->pTable[i][2]="YES";
    } else aManager->pTable[i][2]="";

    /*
      Whether the file is encrypted.
    */
    if (aManager->pHeader[8*i+9]==1)
    {
      aManager->pTable[i][3]="YES";
    } else aManager->pTable[i][3]="";

  /*
    Size translator :).
  */

    k=0;

    if (lFileLength/1048576>0)
    {
      lFileLength/=1048576;
      l=lFileLength;

      while (l>0)
      {
        k++;
        l/=10;
      }

      memcpy(aManager->pTable[i][1]+k, " MiB", 5);
    } else if (lFileLength/1024>0) {
      lFileLength/=1024;
      l=lFileLength;

      while (l>0)
      {
        k++;
        l/=10;
      }

      memcpy(aManager->pTable[i][1]+k, " KiB", 5);
    } else if (lFileLength>0) {
      l=lFileLength;

      while (l>0)
      {
        k++;
        l/=10;
      }

      memcpy(aManager->pTable[i][1]+k, " B", 3);
    } else {
      memcpy(aManager->pTable[i][1], "0 B", 4);
    }

    while (k>0)
    {
      k--;
      aManager->pTable[i][1][k]='0'+lFileLength%10;
      lFileLength/=10;
    }
  }

  return aManager->pTable;
}

int managerMergeFiles (manager* aManager1, manager* aManager2, char* aFileName)
{
  int lCount1;
  int lCount2;
  int lLength;
  int i;
  FILE* lFile;
  char* lBuffer;

  lCount1=0;
  memcpy(&lCount1, aManager1->pHeader, 2);

  lCount2=0;
  memcpy(&lCount2, aManager2->pHeader, 2);

  memcpy(&lLength, aManager1->pHeader+8*lCount1+2, 4);

  /*
    Enlarge the archive header.
  */
  aManager1->pHeader=(char*)realloc(aManager1->pHeader,
                                    (8*(lCount1+lCount2)+6)*sizeof(char));

  /*
    Merge the two headers.
  */
  memcpy(aManager1->pHeader+8*lCount1+2, aManager2->pHeader+2, 8*lCount2+4);

  /*
    Offset the files from the first file.
  */
  for (i=0; i<lCount1; i++)
  {
    *(unsigned int*)(aManager1->pHeader+8*i+2)+=8*lCount2;
  }

  /*
    Offset the files from the second file.
  */
  for (i=0; i<lCount2+1; i++)
  {
    *(unsigned int*)(aManager1->pHeader+8*lCount1+8*i+2)+=lLength-6;
  }

  /*
    Seek the content
  */
  fseek(aManager1->pFile, 8*lCount1+6, SEEK_SET);
  fseek(aManager2->pFile, 8*lCount2+6, SEEK_SET);

  /*
    Adjust the number of files.
  */
  lCount1+=lCount2;
  memcpy(aManager1->pHeader, &lCount1, 2);

  /*
    Prepare the output file.
  */
  lFile=fopen(aFileName, "w");
  fwrite(aManager1->pHeader, 1, 8*lCount1+6, lFile);

  lBuffer=(char*)malloc(BUFFERSIZE*sizeof(char));

  /*
    Fill in the content from the first file.
  */
  while(lLength=fread(lBuffer, 1, BUFFERSIZE, aManager1->pFile))
  {
    fwrite(lBuffer, 1, lLength, lFile);
  }

  /*
    Fill in the content from the second file.
  */
  while(lLength=fread(lBuffer, 1, BUFFERSIZE, aManager2->pFile))
  {
    fwrite(lBuffer, 1, lLength, lFile);
  }

  fclose(lFile);

  return 0;
}

int managerRemoveFiles (manager* aManager, char** aList, char* aFileName)
{
  unsigned int i, j, lCount, lCountNew, lOffset;
  char* lHeader;
  char* lBuffer;
  FILE* lFile;

  lCount=0;
  lCountNew=0;
  lOffset=0;

  /*
    Offset for the remaining files.
  */
  while (aList[i]!=0)
  {
    lOffset+=8;
    i++;
  }

  memcpy(&lCount, aManager->pHeader, 2);

  lHeader=(char*)malloc(8*lCount+6);

  /*
    Create new header. Offset the remaining files.
  */
  for (i=0; i<lCount; i++)
  {
    int lRemove=0;
    j=0;
    while (aList[j]!=0 && lRemove==0)
    {
      if (strcmp(aManager->pTable[i][0], aList[j])==0)
        lRemove=1;
      j++;
    }

    if (lRemove)
    {
      lOffset+=*(unsigned int*)(aManager->pHeader+8*i+10)-
               *(unsigned int*)(aManager->pHeader+8*i+2);
    } else {
      memcpy(lHeader+8*lCountNew+2, aManager->pHeader+8*i+2, 8);
      *(unsigned int*)(lHeader+8*lCountNew+2)-=lOffset;
      lCountNew++;
    }
  }
  
  memcpy(lHeader, &lCountNew, 2);
  *(unsigned int*)(lHeader+8*lCountNew+2)=
    *(unsigned int*)(aManager->pHeader+8*lCount+2)-
    lOffset;

  /*
    Write new header.
  */
  lFile=fopen(aFileName, "w+");
  fwrite(lHeader, 1, 8*lCountNew+6, lFile);

  /*
    Transfer only the remaining files.
  */
  for (i=0; i<lCount; i++)
  {
    int lRemove=0;
    j=0;

    while (aList[j]!=0 && lRemove==0)
    {
      if (strcmp(aManager->pTable[i][0], aList[j])==0)
	lRemove=1;
      j++;
    }

    if (lRemove==0)
    {
      lBuffer=(char*)malloc(*(unsigned int*)(aManager->pHeader+8*i+10)-
                            *(unsigned int*)(aManager->pHeader+8*i+2));

      fseek(aManager->pFile,
            *(unsigned int*)(aManager->pHeader+8*i+2),
            SEEK_SET);

      fread(lBuffer,
            1,
            *(unsigned int*)(aManager->pHeader+8*i+10)-
            *(unsigned int*)(aManager->pHeader+8*i+2),
            aManager->pFile);

      fwrite(lBuffer,
             1,
             *(unsigned int*)(aManager->pHeader+8*i+10)-
             *(unsigned int*)(aManager->pHeader+8*i+2),
             lFile);

      free(lBuffer);
    }
  }

  /*
    Close the output file.
  */
  if (aManager->pFile!=0) fclose(aManager->pFile);
  fclose(lFile);

  free(lHeader);
}

/*
  Get rid of allocated tables. //MOSTLY UNNECESSARY
*/
void managerDestructor (manager* aManager)
{
  int i, j;

  for (i=0; i<4; i++)
  {
    for (j=0; aManager->pTable[i][j]!=0; j++)
      free(aManager->pTable[i][j]);
    free(aManager->pTable[i]);
  }

  free(aManager->pTable);
  free(aManager->pHeader);

  fclose(aManager->pFile);
}
